/**
 * 
 */
package edu.cmu.vlis.wassup.coordinator;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;


/**
 * Coordinator class which coordinates all the actions in the
 * system. Currently works only on a single node. Will be 
 * implemented as primary-backup later on. 
 */
public class Coordinator {

    Set<Node> liveNodes;
    List<Node> allNodes;
    List<Process> allProcesses;
    private int nodeCounter;
    
    public Coordinator() {

        loadProperties();
        this.liveNodes = new HashSet<Node>();
        this.allNodes = new ArrayList<Node>();
        this.allProcesses = new ArrayList<Process>();
        this.nodeCounter = -1;
    }

    public static void main(String[] args) {


        Coordinator coord = new Coordinator();
        coord.initialize();
        while(true){
            coord.pollNodes();
            coord.pollProcesses();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    private void initialize(){

        String[] ipAddressess = System.getProperty("workerNodes").split(",");

        for (String ipAdd : ipAddressess){
            Node n = new Node(ipAdd.trim(),50000);
            this.allNodes.add(n);
            if(n.startSocket())
                this.liveNodes.add(n);            
        }

        String[] processes = System.getProperty("ports").split(",");
        String[] names = System.getProperty("Ids").split(",");
        for(int i=0; i< processes.length; i++){
            Process p = new Process(allNodes.get(i%ipAddressess.length),Integer.parseInt(processes[i]),names[i]);
            p.startSocket();
            this.allProcesses.add(p);            
        }
        System.out.println("Coordinator initialized..");
    }

    private void pollProcesses(){

        for(Process p: this.allProcesses){
            System.out.println("Pinging: "+p.getHost().getIpAdd()+":"+p.getPort());
            if(!p.isAlive()){
                System.err.printf("Process at %s:%d is dead.",p.getHost().getIpAdd(),p.getPort());
                System.out.println();
                Node liveNode = getLiveNode();
                if(liveNode == null){
                    System.err.println("Not able to connect to any live node. Process wont start anywhere");
                    continue;
                }
                boolean resp = liveNode.startRemoteProcess(p.getPort());
                if(resp){
                    System.out.println("Process started successfully at: "+liveNode.getIpAdd()+":"+p.getPort());
                    p.setHost(liveNode);
                    p.startSocket();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    StringBuilder restartMsg = new StringBuilder("Restart:");
                    for(Process process : this.allProcesses)
                        restartMsg.append(process.getHost().getIpAdd());
                    
                    for(Process process : this.allProcesses){
                        boolean r = process.sendRestartMsg(restartMsg.toString());
                        if(r)
                            System.out.println("Message sent to: "+process.getHost().getIpAdd()+":"+process.getPort());
                        else
                            System.out.println("Failed to send Message to: "+process.getHost().getIpAdd()+":"+process.getPort());
                    }
                }
            }
            System.out.println("Process is alive at: "+p.getHost().getIpAdd()+":"+p.getPort());
        }
    }

    private Node getLiveNode(){

        if(this.liveNodes.isEmpty())
            return null;

        this.nodeCounter ++;
        return this.liveNodes.toArray(new Node[0])[this.nodeCounter%this.liveNodes.size()];

    }

    private void pollNodes(){

        List<Node> deadNodes = new ArrayList<Node>(); 
        for(Node n : this.allNodes){
            System.out.println("Pinging node: "+n.getIpAdd());
            if(!n.isAlive()){
                System.out.printf("Node  %s is dead.",n.getIpAdd());
                System.out.println();
                if(!n.startSocket())
                    deadNodes.add(n);
                else
                    this.liveNodes.add(n);
            }
            else
                System.out.println("Node is alive: "+n.getIpAdd());
        }
        this.liveNodes.removeAll(deadNodes);
    }

    private void loadProperties(){
        Properties props = new Properties();
        try {
            props.load(new FileInputStream("src/properties"));
            System.getProperties().putAll(props);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
